﻿// ****************************************************************************** //
//             Module for all functions and variables used to move                // 
//                           the river objects properly.                          //
//      The module handles also the logic to check if the frog takes properly     //
//              the ride onto a trunk or a leaf or instead takes a dip            //
//                       in the water and, thus, loses a life                     //
// ****************************************************************************** //

module RiverSimulation

open Microsoft.Xna.Framework
open Microsoft.Xna.Framework.Input
open Microsoft.Xna.Framework.Graphics
open Utilities
open Casanova
open Coroutines
open Math
open SI
open RiverTypes
open FrogSimulation
open FrogTypes
open MapGrid
open GameState

let mutable positive_collision: bool = false //positive collision true if the frog is taking a ride
let mutable splash_trigger : bool = false    //need this bool variable to trigger the splash sound
let mutable VelFrog = 0.0f;

// ****************************************************************************** //
//        Main update function to handle river movements and collisions           //
// ****************************************************************************** //
let rec update_state(dt:float32) = 
  
  if starting_position = true then splash_trigger <- false

  //for cycle to check if there is a positive collision (frog takes a ride)
  //it removes also river objects when they go outside the screen boundaries
  for c in !river_state.Trunks1 do
    if game_state.river_position = true
        then
            if c.box.Intersects(frog_state.sphere) = true
                then 
                    positive_collision <- true
                    VelFrog <- c.VelX;

    update_trunks c dt
    river_state.Trunks1 := [
        for a in !river_state.Trunks1 do
            if !a.PositionX < -SPAWNING_X then
                yield a]

  for c in !river_state.Trunks2 do
    if game_state.river_position = true
        then
            if c.box.Intersects(frog_state.sphere) = true
                then 
                    positive_collision <- true
                    VelFrog <- c.VelX;

    update_trunks c dt
    river_state.Trunks2 := [
        for a in !river_state.Trunks2 do
            if !a.PositionX < -SPAWNING_X then
                yield a]


  for c in !river_state.Leaf1 do
    if game_state.river_position = true
        then
            if c.box.Intersects(frog_state.sphere) = true
                then 
                    positive_collision <- true
                    VelFrog <- c.VelX;

    update_leaf c dt
    river_state.Leaf1 := [
        for a in !river_state.Leaf1 do
            if !a.PositionX > SPAWNING_X then
                yield a ]

  for c in !river_state.Leaf2 do
    if game_state.river_position = true
        then
            if c.box.Intersects(frog_state.sphere) = true
                then 
                    positive_collision <- true
                    VelFrog <- c.VelX;

    update_leaf c dt
    river_state.Leaf2 := [
        for a in !river_state.Leaf2 do
            if !a.PositionX > SPAWNING_X then
                yield a]
  
  if game_state.river_position = true
        then
            if positive_collision = true
                then
                    frog_state.VelX <- dt * VelFrog
                    positive_collision <- false
                    splash_trigger <- false
            else
                    splash_trigger <- true    
                    game_state.Life <- game_state.Life - 1
                    game_state.time_counter <- 0.0f
                    FrogSimulation.reset_state()
                    starting_position <- true
  

//update function to give trunks some speed
and private update_trunks (c:Trunk) (dt:float32) =
  c.PositionX := !c.PositionX + dt * c.VelX
  c.box.Min.X <- c.box.Min.X + dt * c.VelX
  c.box.Max.X <- c.box.Max.X + dt * c.VelX

//update function to give leafs some speed
and private update_leaf (c:Leaf) (dt:float32) =
  c.PositionX := !c.PositionX + dt * c.VelX
  c.box.Min.X <- c.box.Min.X + dt * c.VelX
  c.box.Max.X <- c.box.Max.X + dt * c.VelX


let private (!) = immediate_read
let private (:=) = immediate_write

//function (coroutine) to create new trunks
let spawn_trunks() = 
    co{
      do! yield_

      if river_state.Trunks1.Value.IsEmpty //if no trunk has been created yet
        then //create a row of trunks
        river_state.Trunks1 :=
        { 

          PositionX = variable(SPAWNING_X)
          PositionY = variable(RIV1_Y)
          PositionZ = variable(RIV1_Z)
          box = new BoundingBox(new Vector3(SPAWNING_X + (-4.2f),RIV1_Y + (1.3f), RIV1_Z + (2.0f)),new Vector3(SPAWNING_X + 4.2f,RIV1_Y-(0.8f),RIV1_Z + (-1.0f)))
          VelX = SPEED_RIV1
        } :: !river_state.Trunks1

      if not river_state.Trunks1.Value.IsEmpty
        then
        let mutable a : Trunk = river_state.Trunks1.Value.Head //get the first trunk of the row

        if a.PositionX.Value > SPAWN_LIMIT_RIV1 //if the first trunk is almost going outside the screen
            then //time to create a new trunk
            river_state.Trunks1 :=
            { 
              PositionX = variable(SPAWNING_X)
              PositionY = variable(RIV1_Y)
              PositionZ = variable(RIV1_Z)
              box = new BoundingBox(new Vector3(SPAWNING_X + (-4.2f),RIV1_Y + (1.3f), RIV1_Z + (2.0f)),new Vector3(SPAWNING_X + 4.2f,RIV1_Y-(0.8f),RIV1_Z + (-1.0f)))
              VelX = SPEED_RIV1
            } :: !river_state.Trunks1

      
      if river_state.Trunks2.Value.IsEmpty
        then 
        river_state.Trunks2 :=
        { 

          PositionX = variable(SPAWNING_X)
          PositionY = variable(RIV3_Y)
          PositionZ = variable(RIV3_Z)
          box = new BoundingBox(new Vector3(SPAWNING_X,RIV3_Y + (1.3f), RIV3_Z + (2.0f)),new Vector3(SPAWNING_X + 8.2f,RIV3_Y-(0.8f),RIV3_Z + (-1.0f)))
          VelX = SPEED_RIV3
        } :: !river_state.Trunks2

      if not river_state.Trunks2.Value.IsEmpty
        then
        let mutable a : Trunk = river_state.Trunks2.Value.Head

        if a.PositionX.Value > SPAWN_LIMIT_RIV3
            then
            river_state.Trunks2 :=
            { 
              PositionX = variable(SPAWNING_X)
              PositionY = variable(RIV3_Y)
              PositionZ = variable(RIV3_Z)
              box = new BoundingBox(new Vector3(SPAWNING_X,RIV3_Y + (1.3f), RIV3_Z + (2.0f)),new Vector3(SPAWNING_X + 8.2f,RIV3_Y-(0.8f),RIV3_Z + (-1.0f)))
              VelX = SPEED_RIV3
            } :: !river_state.Trunks2
          
            
      return()
      }

//function (coroutine) to create new trunks
let spawn_leafs() = 
    co{
      do! yield_

      if river_state.Leaf1.Value.IsEmpty
        then 
        river_state.Leaf1 :=
        { 

          PositionX = variable(-SPAWNING_X)
          PositionY = variable(RIV2_Y)
          PositionZ = variable(RIV2_Z)
          box = new BoundingBox(new Vector3(-SPAWNING_X - 5.2f,RIV2_Y + (1.3f), RIV2_Z + (2.0f)),new Vector3(-SPAWNING_X + 7.8f,RIV2_Y-(0.8f),RIV2_Z + (-1.0f)))
          VelX = SPEED_RIV2
        } :: !river_state.Leaf1

      if not river_state.Leaf1.Value.IsEmpty
        then
        let mutable a : Leaf = river_state.Leaf1.Value.Head

        if a.PositionX.Value < SPAWN_LIMIT_RIV2
            then
            river_state.Leaf1 :=
            { 
              PositionX = variable(-SPAWNING_X)
              PositionY = variable(RIV2_Y)
              PositionZ = variable(RIV2_Z)
              box = new BoundingBox(new Vector3(-SPAWNING_X - 5.2f,RIV2_Y + (1.3f), RIV2_Z + (2.0f)),new Vector3(-SPAWNING_X + 7.8f,RIV2_Y-(0.8f),RIV2_Z + (-1.0f)))
              VelX = SPEED_RIV2
            } :: !river_state.Leaf1

      
      if river_state.Leaf2.Value.IsEmpty
        then 
        river_state.Leaf2 :=
        { 

          PositionX = variable(-SPAWNING_X)
          PositionY = variable(RIV4_Y)
          PositionZ = variable(RIV4_Z)
          box = new BoundingBox(new Vector3(-SPAWNING_X - 5.2f,RIV4_Y + (1.3f), RIV4_Z + (2.0f)),new Vector3(-SPAWNING_X + 7.8f,RIV4_Y-(0.8f),RIV4_Z + (-1.0f)))
          VelX = SPEED_RIV4
        } :: !river_state.Leaf2

      if not river_state.Leaf2.Value.IsEmpty
        then
        let mutable a : Leaf = river_state.Leaf2.Value.Head

        if a.PositionX.Value < SPAWN_LIMIT_RIV4
            then
            river_state.Leaf2 :=
            { 
              PositionX = variable(-SPAWNING_X)
              PositionY = variable(RIV4_Y)
              PositionZ = variable(RIV4_Z)
              box = new BoundingBox(new Vector3(-SPAWNING_X - 5.2f,RIV4_Y + (1.3f), RIV4_Z + (2.0f)),new Vector3(-SPAWNING_X + 7.8f,RIV4_Y-(0.8f),RIV4_Z + (-1.0f)))
              VelX = SPEED_RIV4
            } :: !river_state.Leaf2
          
            
      return()
      }
